﻿using MiniFox.Infrastructure;
using MiniFox.Utility;
using Newtonsoft.Json;

namespace MiniFox.Platform.Jwt
{
    /// <summary>
    /// JWT
    /// </summary>
    public class JsonWebToken : Ticket
    {
        new const char SPLITER = '.';
        TokenHeader _header;
        TokenPayload _payload;
        string _signature;

        #region 构造
        /// <summary>
        /// 
        /// </summary>
        /// <param name="appId"></param>
        /// <param name="issuer"></param>
        public JsonWebToken(string appId, string issuer) : this(appId, issuer, DateTimeOffset.Now)
        {
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="appId"></param>
        /// <param name="issuer"></param>
        /// <param name="duration"></param>
        public JsonWebToken(string appId, string issuer, int duration) : this(appId, issuer, DateTimeOffset.Now, duration)
        {
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="appId"></param>
        /// <param name="issuer"></param>
        /// <param name="createTime"></param>
        public JsonWebToken(string appId, string issuer, DateTimeOffset createTime) : this(appId, issuer, createTime, 3600)
        {
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="appId"></param>
        /// <param name="issuer"></param>
        /// <param name="createTime"></param>
        /// <param name="duration"></param>
        public JsonWebToken(string appId, string issuer, DateTimeOffset createTime, int duration) : this(SequenceGUID.NewGUID(), appId, issuer, createTime, duration)
        {
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="ticketId"></param>
        /// <param name="appId"></param>
        /// <param name="issuer"></param>
        /// <param name="createTime"></param>
        /// <param name="duration"></param>
        public JsonWebToken(string ticketId, string appId, string issuer, DateTimeOffset createTime, int duration) : this(new TokenHeader(), ticketId, appId, issuer, createTime, duration)
        {
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="header"></param>
        /// <param name="appId"></param>
        /// <param name="issuer"></param>
        public JsonWebToken(TokenHeader header, string appId, string issuer) : this(header, appId, issuer, DateTimeOffset.Now, 3600)
        {
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="header"></param>
        /// <param name="appId"></param>
        /// <param name="issuer"></param>
        /// <param name="duration"></param>
        public JsonWebToken(TokenHeader header, string appId, string issuer, int duration) : this(header, appId, issuer, DateTimeOffset.Now, duration)
        {
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="header"></param>
        /// <param name="appId"></param>
        /// <param name="issuer"></param>
        /// <param name="createTime"></param>
        public JsonWebToken(TokenHeader header, string appId, string issuer, DateTimeOffset createTime) : this(header, appId, issuer, createTime, 3600)
        {
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="header"></param>
        /// <param name="appId"></param>
        /// <param name="issuer"></param>
        /// <param name="createTime"></param>
        /// <param name="duration"></param>
        public JsonWebToken(TokenHeader header, string appId, string issuer, DateTimeOffset createTime, int duration) : this(header, SequenceGUID.NewGUID(), appId, issuer, createTime, duration)
        {
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="header"></param>
        /// <param name="ticketId"></param>
        /// <param name="appId"></param>
        /// <param name="issuer"></param>
        /// <param name="createTime"></param>
        /// <param name="duration"></param>
        public JsonWebToken(TokenHeader header, string ticketId, string appId, string issuer, DateTimeOffset createTime, int duration) : base(ticketId, createTime, duration)
        {
            _header = header;
            _payload = new TokenPayload
            {
                TicketID = ticketId,
                ApplicationID = appId,
                Issuer = issuer,
                CreationTime = createTime,
                Duration = duration
            };
        }

        private JsonWebToken(TokenHeader header, TokenPayload payload, string signature) : base(payload.TicketID, payload.CreationTime, payload.Duration)
        {
            _header = header;
            _payload = payload;
            _signature = signature;
        }
        #endregion

        /// <summary>
        /// 
        /// </summary>
        public TokenHeader Header
        {
            get { return _header; }
        }
        /// <summary>
        /// 
        /// </summary>
        public TokenPayload Payload
        {
            get { return _payload; }
        }
        /// <summary>
        /// 
        /// </summary>
        protected internal string TokenString
        {
            get { return $"{this.Header}{SPLITER}{this.Payload}"; }
        }
        /// <summary>
        /// 
        /// </summary>
        public string Signature
        {
            get { return _signature; }
            internal set { _signature = value; }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return $"{this.TokenString}{SPLITER}{(this.Signature ?? string.Empty).UrlEncode()}";
        }

        internal string JsonValue
        {
            get
            {
                return $"{this.Header.JsonValue}{SPLITER}{this.Payload.JsonValue}";
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="token"></param>
        /// <returns></returns>
        public static implicit operator string(JsonWebToken token)
        {
            if (token == null)
                return null;
            return token.ToString();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="token"></param>
        public static implicit operator JsonWebToken(string token)
        {
            if (string.IsNullOrEmpty(token))
                return null;
            string[] datas = token.Split(SPLITER);
            TokenHeader header = datas[0];
            TokenPayload payload = datas[1];
            string signature = datas[2];
            JsonWebToken accessToken = new JsonWebToken(header, payload, signature);
            return accessToken;
        }
    }
    /// <summary>
    /// 
    /// </summary>
    public class TokenHeader
    {
        string _type;
        SignatureAlgorithm _alg;
        /// <summary>
        /// 
        /// </summary>
        public TokenHeader() : this(SignatureAlgorithm.HS256, "JWT")
        {

        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="alg"></param>
        public TokenHeader(SignatureAlgorithm alg) : this(alg, "JWT")
        {

        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="alg"></param>
        /// <param name="type"></param>
        public TokenHeader(SignatureAlgorithm alg, string type)
        {
            _alg = alg;
            _type = type;
        }
        /// <summary>
        /// 
        /// </summary>
        [JsonProperty("alg"), JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
        public SignatureAlgorithm Algorithm
        {
            get { return _alg; }
        }
        /// <summary>
        /// 
        /// </summary>
        [JsonProperty("typ")]
        public string Type
        {
            get { return _type; }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            string json = JsonValue;
            return json.ToBase64String().UrlEncode();
        }

        internal string JsonValue
        {
            get
            {
                return this.ToJsonString();
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="header"></param>
        public static implicit operator TokenHeader(string header)
        {
            header = header.UrlDecode();
            header = header.FromBase64String();
            return header.ToJsonObject<TokenHeader>();
        }
    }
    /// <summary>
    /// 
    /// </summary>
    public class TokenPayload
    {
        /// <summary>
        /// 
        /// </summary>
        public TokenPayload()
        {
        }
        /// <summary>
        /// 
        /// </summary>
        [JsonProperty("jti")]
        public string TicketID
        {
            get;
            set;
        }
        /// <summary>
        /// 
        /// </summary>
        [JsonProperty("aud")]
        public string ApplicationID
        {
            get;
            set;
        }
        /// <summary>
        /// 
        /// </summary>
        [JsonProperty("iss")]
        public string Issuer
        {
            get;
            set;
        }
        /// <summary>
        /// 
        /// </summary>
        [JsonProperty("sub")]
        public string Subscriber
        {
            get;
            set;
        }
        /// <summary>
        /// 
        /// </summary>
        [JsonProperty("iat"), JsonConverter(typeof(DateTimeJsonConverter), GlobalStandardFormat.COMPLETE_DATETIME_OFFSET_FORMAT)]
        public DateTimeOffset CreationTime
        {
            get;
            set;
        }
        /// <summary>
        /// 
        /// </summary>
        [JsonProperty("nbf"), JsonConverter(typeof(DateTimeJsonConverter), GlobalStandardFormat.COMPLETE_DATETIME_OFFSET_FORMAT)]
        public DateTimeOffset AvailableTime
        {
            get { return this.CreationTime; }
        }
        ///// <summary>
        ///// 
        ///// </summary>
        //[JsonProperty("exp"), JsonConverter(typeof(DateTimeJsonConverter), GlobalStandardFormat.COMPLETE_DATETIME_OFFSET_FORMAT)]
        //public DateTimeOffset ExpirationTime
        //{
        //    get { return this.CreationTime.AddSeconds(this.Duration); }
        //}

        /// <summary>
        /// 
        /// </summary>
        [JsonProperty("exp")]
        public int Duration
        {
            get;
            set;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            string json = JsonValue;
            return json.ToBase64String().UrlEncode();
        }
        internal string JsonValue
        {
            get
            {
                return this.ToJsonString();
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="payload"></param>
        public static implicit operator TokenPayload(string payload)
        {
            payload = payload.UrlDecode();
            payload = payload.FromBase64String();
            return payload.ToJsonObject<TokenPayload>();
        }
    }
    /// <summary>
    /// 
    /// </summary>
    public enum SignatureAlgorithm
    {
        /// <summary>
        /// 
        /// </summary>
        MD5,
        /// <summary>
        /// 
        /// </summary>
        SHA1,
        /// <summary>
        /// 
        /// </summary>
        SHA256,
        /// <summary>
        /// 
        /// </summary>
        SHA384,
        /// <summary>
        /// 
        /// </summary>
        SHA512,
        /// <summary>
        /// 
        /// </summary>
        HS1,
        /// <summary>
        /// 
        /// </summary>
        HS256,
        /// <summary>
        /// 
        /// </summary>
        HS384,
        /// <summary>
        /// 
        /// </summary>
        HS512,
        /// <summary>
        /// 
        /// </summary>
        DES,
        /// <summary>
        /// 
        /// </summary>
        AES,
        /// <summary>
        /// 
        /// </summary>
        RC2,
        /// <summary>
        /// 
        /// </summary>
        Rijndael,
        /// <summary>
        /// 
        /// </summary>
        TripleDES
    }
}
